﻿using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using System.Threading.Tasks;
using Urs.Core;
using Urs.Core.Caching;
using Urs.Data.Domain.Stores;
using Urs.Data.Domain.Users;
using Urs.Data.Domain.Media;
using Urs.Data.Domain.Orders;
using Urs.Data.Domain.Shipping;
using Urs.Data.Domain.Configuration;
using Plugin.Api.Extensions;
using Plugin.Api.Models.Media;
using Plugin.Api.Models.Order;
using Urs.Services.Stores;
using Urs.Services.Users;
using Urs.Services.Localization;
using Urs.Services.Logging;
using Urs.Services.Media;
using Urs.Services.Orders;
using Urs.Services.Payments;
using Urs.Services.Shipping;
using Urs.Framework;
using Urs.Framework.Controllers;

namespace Plugin.Api.Controllers
{
    /// <summary>
    /// 订单接口
    /// </summary>
    [ApiAuthorize]
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/order")]
    [ApiController]
    public class OrderController : BaseApiController
    {
        private readonly MediaSettings _mediaSettings;
        private readonly OrderSettings _orderSettings;
        private readonly AddressSettings _addressSettings;
        private readonly StoreSettings _storeSettings;
        private readonly BillingAddressSettings _billingAddressSettings;
        private readonly ILocalizationService _localizationService;
        private readonly ILogger _logger;
        private readonly IWorkContext _workContext;
        private readonly IWebHelper _webHelper;
        private readonly ICacheManager _cacheManager;
        private readonly IPictureService _pictureService;
        private readonly IPaymentService _paymentService;
        private readonly IOrderService _orderService;
        private readonly IGoodsSpecParser _goodsSpecParser;
        private readonly IOrderTotalCalculationService _orderTotalCalculationService;
        private readonly IOrderProcessingService _orderProcessingService;
        private readonly IUserService _userService;
        private readonly IShipmentService _shipmentService;

        /// <summary>
        /// 构造器
        /// </summary>
        public OrderController(MediaSettings mediaSettings,
            OrderSettings orderSettings,
            AddressSettings addressSettings,
            StoreSettings storeSettings,
            BillingAddressSettings billingAddressSettings,
            ILocalizationService localizationService,
            ILogger logger,
            IWorkContext workContext,
            IWebHelper webHelper,
            ICacheManager cacheManager,
            IPictureService pictureService,
            IPaymentService paymentService,
            IOrderService orderService,
            IGoodsSpecParser goodsSpecParser,
            IOrderTotalCalculationService orderTotalCalculationService,
            IOrderProcessingService orderProcessingService,
            IUserService userService,
            IShipmentService shipmentService)
        {
            this._mediaSettings = mediaSettings;
            this._orderSettings = orderSettings;
            this._addressSettings = addressSettings;
            this._storeSettings = storeSettings;
            this._billingAddressSettings = billingAddressSettings;
            this._localizationService = localizationService;
            this._logger = logger;
            this._workContext = workContext;
            this._webHelper = webHelper;
            this._cacheManager = cacheManager;
            this._pictureService = pictureService;
            this._paymentService = paymentService;
            this._orderService = orderService;
            this._goodsSpecParser = goodsSpecParser;
            this._orderTotalCalculationService = orderTotalCalculationService;
            this._orderProcessingService = orderProcessingService;
            this._userService = userService;
            this._shipmentService = shipmentService;
        }

        #region Utilities

        [NonAction]
        protected MoPicture PreparePictureModel(Goods goods,
            int pictureSize, bool showDefaultPicture, string goodsName)
        {
            if (goods == null)
                throw new ArgumentNullException("goods");

                //first try to load goods piture
                //todo
                Picture picture = _pictureService.GetPicturesByGoodsId(goods.Id, 1).FirstOrDefault(); ;
                return new MoPicture()
                {
                    NormalUrl = _pictureService.GetPictureUrl(picture, pictureSize, showDefaultPicture),
                };
        }

        protected MoOrderList PrepareUserOrderListModel(User user, DateTime? startTime, DateTime? endTime, OrderStatus? os, int page, int size)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            var model = new MoOrderList();

            if (size <= 0) size = size > 0 ? size : 10;
            if (page <= 0) page = 1;

            var orders = _orderService.SearchOrders(userId: user.Id, startTime: startTime, endTime: endTime, os: os, pageIndex: page - 1, pageSize: size);
            model.Paging.Page = page;
            model.Paging.Size = size;
            model.Paging.TotalPage = orders.TotalPages;
            foreach (var order in orders)
            {
                var orderModel = new MoOrderList.MoOrderDetail()
                {
                    OrderId = order.Id,
                    Code = order.Code,
                    OrderGuid = order.OrderGuid.ToString(),
                    PaymentMethod = _localizationService.GetResource(order.PaymentMethodSystemName),
                    CreatedTime = order.CreateTime,
                    OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService),
                    OrderStatusId = order.OrderStatusId,
                    IsReturn = _orderProcessingService.IsAfterSalesAllowed(order),
                    IsComplete = order.OrderStatus == OrderStatus.Processing || order.OrderStatus == OrderStatus.Delivering,
                    IsDelete = _orderProcessingService.IsDeleteAllowed(order),
                    IsReview = _orderProcessingService.IsReviewAllowed(order),
                    IsCancel = _orderProcessingService.IsCancelAllowed(order)
                };

                orderModel.Address.PrepareModel(order.ShippingAddress, false, _addressSettings);

                foreach (var op in order.orderItems)
                {
                    var picture = PreparePictureModel(op.Goods,
                        _mediaSettings.CartThumbPictureSize, true, op.GoodsName);

                    var ogoods = new MoOrderList.MoOrderDetail.MoOrderGoods();
                    ogoods.GoodsId = op.GoodsId;
                    ogoods.Name = op.GoodsName;
                    ogoods.Sku = op.Sku;
                    ogoods.AttributesXml = op.AttributeDescription;
                    ogoods.UnitPrice = PriceFormatter.FormatPrice(op.UnitPrice);
                    ogoods.Qty = op.Quantity;
                    ogoods.Picture = picture;

                    orderModel.Items.Add(ogoods);
                }
                orderModel.OrderTotal = PriceFormatter.FormatPrice(order.OrderTotal);

                model.Items.Add(orderModel);
            }

            return model;
        }

        protected MoPTOrderList PreparePTUserOrderListModel(User user, DateTime? startTime, DateTime? endTime, OrderStatus? os, int page, int size)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            var model = new MoPTOrderList();

            if (size <= 0) size = size > 0 ? size : 10;
            if (page <= 0) page = 1;

            var orders = _orderService.SearchOrders(userId: user.Id, startTime: startTime, endTime: endTime, os: os, pageIndex: page - 1, pageSize: size);
            model.Paging.Page = page;
            model.Paging.Size = size;
            model.Paging.TotalPage = orders.TotalPages;
            foreach (var order in orders)
            {
                var orderModel = new MoPTOrderList.MoOrderDetail()
                {
                    OrderId = order.Id,
                    Code = order.Code,
                    OrderGuid = order.OrderGuid.ToString(),
                    PaymentMethod = _localizationService.GetResource(order.PaymentMethodSystemName),
                    CreatedTime = order.CreateTime,
                    OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService),
                    OrderStatusId = order.OrderStatusId,
                    IsReturn = _orderProcessingService.IsAfterSalesAllowed(order),
                    IsComplete = order.OrderStatus == OrderStatus.Processing || order.OrderStatus == OrderStatus.Delivering,
                    IsDelete = _orderProcessingService.IsDeleteAllowed(order),
                    IsReview = _orderProcessingService.IsReviewAllowed(order),
                    IsCancel = _orderProcessingService.IsCancelAllowed(order)
                };
                orderModel.Address.PrepareModel(order.ShippingAddress, false, _addressSettings);

                foreach (var op in order.orderItems)
                {
                    var picture = PreparePictureModel(op.Goods,
                        _mediaSettings.CartThumbPictureSize, true, op.GoodsName);

                    var ogoods = new MoPTOrderList.MoOrderDetail.MoOrderGoods();
                    ogoods.GoodsId = op.GoodsId;
                    ogoods.Name = op.GoodsName;
                    ogoods.Sku = op.Sku;
                    ogoods.AttributesXml = op.AttributeDescription;
                    ogoods.UnitPrice = PriceFormatter.FormatPrice(op.UnitPrice);
                    ogoods.Qty = op.Quantity;
                    ogoods.Picture = picture;

                    orderModel.Items.Add(ogoods);
                }
                orderModel.OrderTotal = PriceFormatter.FormatPrice(order.OrderTotal);

                model.Items.Add(orderModel);
            }

            return model;
        }

        protected MoOrderDetail PrepareOrderDetailsModel(Order order)
        {
            if (order == null)
                throw new ArgumentNullException("order");
            var model = new MoOrderDetail();

            model.Id = order.Id;
            model.Code = order.Code;
            model.OrderGuid = order.OrderGuid.ToString();
            model.CreateTime = order.CreateTime.ToString("yyyy-MM-dd HH:mm");
            model.OrderStatus = order.OrderStatus.GetLocalizedEnum(_localizationService);
            model.OrderStatusId = order.OrderStatusId;
            model.IsReturn = _orderProcessingService.IsAfterSalesAllowed(order);
            //shipping info
            model.ShippingStatusId = order.ShippingStatusId;
            model.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_localizationService);
            if (order.ShippingStatus != ShippingStatus.ShippingNotRequired)
            {
                model.IsShippable = true;
                model.ShippingAddress.PrepareModel(order.ShippingAddress, false, _addressSettings);
                model.ShippingMethod = order.ShippingMethod;
                //shipments (only already shipped)
                var shipments = order.Shipments.Where(x => x.ShippedTime.HasValue).OrderBy(x => x.CreateTime).ToList();
                foreach (var shipment in shipments)
                {
                    var shipmentModel = new MoOrderDetail.ShipmentBriefModel()
                    {
                        Id = shipment.Id,
                        TrackingNumber = shipment.TrackingNumber,
                    };
                    if (shipment.ShippedTime.HasValue)
                        shipmentModel.ShippedDate = shipment.ShippedTime.Value;
                    if (shipment.DeliveryTime.HasValue)
                        shipmentModel.DeliveryDate = shipment.DeliveryTime.Value;
                    model.Shipments.Add(shipmentModel);
                }
            }
            //payment method
            var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);
            model.PaymentMethod = paymentMethod != null ? paymentMethod.GetLocalizedFriendlyName(_localizationService) : order.PaymentMethodSystemName;
            //order subtotal
            model.OrderSubtotal = PriceFormatter.FormatPrice(order.OrderSubtotal);
            //order shipping
            model.OrderShipping = PriceFormatter.FormatPrice(order.OrderShipping);
            //payment method additional fee
            var paymentMethodAdditionalFee = order.PaymentMethodAdditionalFee;
            if (paymentMethodAdditionalFee > decimal.Zero)
                model.PaymentMethodAdditionalFee = PriceFormatter.FormatPrice(paymentMethodAdditionalFee);

            //reward points           
            if (order.PointsEntry != null)
            {
                model.RedeemedRewardPoints = -order.PointsEntry.Points;
                model.RedeemedRewardPointsAmount = PriceFormatter.FormatPrice(-(order.PointsEntry.UsedAmount));
            }
            //total
            model.OrderTotal = PriceFormatter.FormatPrice(order.OrderTotal);

            //checkout attributes
            model.CheckoutAttributeInfo = order.CheckoutAttributeDescription;

            //order notes
            foreach (var orderNote in order.OrderNotes
                .Where(on => on.DisplayToUser)
                .OrderByDescending(on => on.CreateTime)
                .ToList())
            {
                model.Notes.Add(new MoOrderDetail.OrderNote()
                {
                    Note = orderNote.FormatOrderNoteText(),
                    Time = orderNote.CreateTime
                });
            }

            var orderItems = _orderService.GetAllorderItems(order.Id, null, null, null, null, null, null);
            foreach (var op in orderItems)
            {
                var picture = PreparePictureModel(op.Goods,
                       _mediaSettings.CartThumbPictureSize, true, op.GoodsName);

                var opModel = new MoOrderDetail.OrderGoods()
                {
                    Id = op.Id,
                    Sku = op.Goods.FormatSku(op.AttributesXml, _goodsSpecParser),
                    GoodsId = op.GoodsId,
                    Quantity = op.Quantity,
                    AttributeInfo = op.AttributeDescription,
                    Picture = picture
                };
                //goods name
                opModel.Name = op.GoodsName;
                model.Items.Add(opModel);
                //unit price, subtotal
                opModel.UnitPrice = PriceFormatter.FormatPrice(op.UnitPrice);
                opModel.SubTotal = PriceFormatter.FormatPrice(op.Price);

            }
            return model;
        }

        #endregion

        /// <summary>
        /// 获取退换货
        /// </summary>
        /// <param name="page">页码（默认：1）</param>
        /// <param name="size">页大小（默认值：12）</param>
        /// <returns></returns>
        [HttpGet("aftersales")]
        public async Task<ApiResponse<MoReturnList>> AfterSales(int page = 1, int size = 12)
        {
            var user = RegisterUser;

            var data = await Task.Run(() =>
            {
                var command = new PagingFiltering
                {
                    PageNumber = page,
                    PageSize = size > 0 ? size : 10
                };
                var model = new MoReturnList();
                var list = _orderService.SearchAfterSales(user.Id, 0, null, command.PageIndex, command.PageSize);
                foreach (var rr in list)
                {
                    var item = new MoReturnList.AfterSales()
                    {
                        Id = rr.Id,
                        Status = rr.AfterSalesStatus.GetLocalizedEnum(_localizationService),
                        Qty = rr.Quantity,
                        Action = rr.RequestedAction,
                        Reason = rr.ReasonForReturn,
                        Comments = rr.UserComments,
                        CreateTime = rr.CreateTime,
                    };
                    var op = _orderService.GetOrderGoodsById(rr.OrderItemId);
                    if (op != null)
                    {
                        item.GoodsId = op.GoodsId;
                        item.GoodsName = op.GoodsName;
                        item.Picture = PreparePictureModel(op.Goods, _mediaSettings.CartThumbPictureSize, true, op.GoodsName);
                    }
                    model.Items.Add(item);

                }
                return model;
            });

            return ApiResponse<MoReturnList>.Success(data);
        }
        /// <summary>
        /// 退货提交
        /// </summary>
        /// <returns></returns>
        [HttpPost("returnpost")]
        public async Task<ApiResponse> ReturnPost(MoReturn moReturn)
        {
            var user = RegisterUser;

            var op = _orderService.GetOrderGoodsById(moReturn.OrderGoodsId);
            var order = _orderService.GetOrderById(moReturn.OrderGoodsId);
            if (order == null && op == null)
                return ApiResponse.Warn("退货商品未选择");

            await Task.Run(() =>
             {
                 _logger.Information("rr new returnRequest");
                 var rr = new AfterSales()
                 {
                     OrderItemId = moReturn.OrderGoodsId,
                     Quantity = moReturn.Qty,
                     UserId = user.Id,
                     ReasonForReturn = moReturn.Reason,
                     RequestedAction = moReturn.Action,
                     UserComments = moReturn.Comments,
                     Images = moReturn.Images,
                     StaffNotes = string.Empty,
                     AfterSalesStatus = AfterSalesStatus.Pending,
                     UpdateTime = DateTime.Now
                 };
                 if (op != null)
                     rr.OrderId = op.OrderId;
                 if (order != null)
                     rr.OrderId = order.Id;
                 _logger.Information("rr insert ");
                 _orderService.InsertAfterSales(rr);
             });

            return ApiResponse.Success();
        }

        /// <summary>
        /// 获取订单列表
        /// </summary>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <param name="orderstatus">订单状态</param>
        /// <param name="page">页码（默认：1）</param>
        /// <param name="size">页大小（默认值：12）</param>
        /// <returns></returns>
        [HttpGet("list")]
        public async Task<ApiResponse<MoOrderList>> List(DateTime? startTime = null, DateTime? endTime = null, int orderstatus = 0, int page = 1, int size = 12)
        {
            var user = RegisterUser;

            var data = await Task.Run(() =>
              {
                  return PrepareUserOrderListModel(user, startTime, endTime, (OrderStatus)orderstatus, page, size);
              });

            return ApiResponse<MoOrderList>.Success(data);
        }

        /// <summary>
        /// 获取订单详情
        /// </summary>
        /// <param name="oid">订单Id</param>
        /// <returns></returns>
        [HttpGet("detail")]
        public async Task<ApiResponse<MoOrderDetail>> Detail(int oid)
        {
            var user = RegisterUser;

            var order = _orderService.GetOrderById(oid);
            if (order == null || order.Deleted || user.Id != order.UserId)
                return ApiResponse<MoOrderDetail>.BadRequest();

            var data = await Task.Run(() =>
              {
                  return PrepareOrderDetailsModel(order);
              });

            return ApiResponse<MoOrderDetail>.Success(data);
        }
        /// <summary>
        /// 完成订单
        /// </summary>
        /// <param name="mo">订单信息</param>
        /// <returns></returns>
        [HttpPost("complete")]
        public async Task<ApiResponse> Complete(MoOrderCancel mo)
        {
            if (!_orderSettings.CancelEnabled)
                return ApiResponse.Warn("订单不允许取消");

            var user = RegisterUser;

            if (!Guid.TryParse(mo.OrderGuid, out Guid orderGuid))
                return ApiResponse.Warn("order guid格式不正确");

            var order = _orderService.GetOrderByGuid(orderGuid);
            if (order == null || order.Deleted || user.Id != order.UserId)
                return ApiResponse.BadRequest();

            _orderProcessingService.Complete(order.Id, false);

            return ApiResponse.Success();
        }

        /// <summary>
        /// 确认收货
        /// </summary>
        /// <param name="mo">确认收货对象/param>
        /// <returns></returns>
        [HttpPost("deliver")]
        public async Task<ApiResponse> Deliver(MoOrderDeliver mo)
        {
            var order = _orderService.GetOrderByCode(mo.Code);

            if (order == null || order.Deleted)
                return ApiResponse.NotFound();

            var shipment = _shipmentService.GetShipmentByOrderId(order.Id);
            if (shipment == null)
                return ApiResponse.Warn("暂无配送");


            if (shipment.DeliveryTime.HasValue)
                return ApiResponse.Warn("订单已签收");

            _orderProcessingService.Deliver(shipment, false);

            return ApiResponse.Success();
        }

        /// <summary>
        /// 取消订单
        /// </summary>
        /// <returns></returns>
        [HttpPost("cancel")]
        public async Task<ApiResponse> GetCancel(MoOrderCancel mo)
        {
            if (!_orderSettings.CancelEnabled)
                return ApiResponse.Warn("订单不允许取消");

            var user = RegisterUser;

            if (!Guid.TryParse(mo.OrderGuid, out Guid orderGuid))
                return ApiResponse.Warn("OrderGuid格式不正确");

            var order = _orderService.GetOrderByGuid(orderGuid);

            if (order == null || order.Deleted || user.Id != order.UserId)
                return ApiResponse.BadRequest();

            _orderProcessingService.CancelOrder(order, false);

            return ApiResponse.Success();
        }
    }
}
